<h2>Why is this an issue?</h2>
<p>Both the <code>List.Exists</code> method and <code>IEnumerable.Any</code> method can be used to find the first element that satisfies a predicate
in a collection. However, <code>List.Exists</code> can be faster than <code>IEnumerable.Any</code> for <code>List</code> objects, as well as requires
significantly less memory. For small collections, the performance difference may be negligible, but for large collections, it can be noticeable. The
same applies to <code>ImmutableList</code> and arrays too.</p>
<p>It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the <a
href="https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-9/#collections">performance improvements in .NET 9</a> have brought
<code>Any</code> closer to the performance of collection-specific <code>Exists</code> methods in most scenarios.</p>
<p><strong>Applies to</strong></p>
<ul>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.exists">List</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.array.exists">Array</a> </li>
  <li> <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.exists">ImmutableList</a> </li>
</ul>
<h3>What is the potential impact?</h3>
<p>We measured at least 3x improvement in execution time. For more details see the <code>Benchmarks</code> section from the <code>More info</code>
tab.</p>
<p>Also, no memory allocations were needed for the <code>Exists</code> method, since the search is done in-place.</p>
<h3>Exceptions</h3>
<p>Since <code><a href="https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities">LINQ to
Entities</a></code> relies a lot on <code>System.Linq</code> for <a
href="https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities#query-conversion">query conversion</a>,
this rule won’t raise when used within LINQ to Entities syntaxes.</p>
<h2>How to fix it</h2>
<p>The <code>Exists</code> method is defined on the collection class, and it has the same signature as <code>Any</code> extension method if a
predicate is used. The method can be replaced in place.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="1" data-diff-type="noncompliant">
Function ContainsEven(data As List(Of Integer)) As Boolean
    Return data.Any(Function(x) x Mod 2 = 0)
End Function
</pre>
<pre data-diff-id="2" data-diff-type="noncompliant">
Function ContainsEven(data() As Integer) As Boolean
    Return data.Any(Function(x) x Mod 2 = 0)
End Function
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
Function ContainsEven(data As List(Of Integer)) As Boolean
    Return data.Exists(Function(x) x Mod 2 = 0)
End Function
</pre>
<pre data-diff-id="2" data-diff-type="compliant">
Function ContainsEven(data() As Integer) As Boolean
    Return Array.Exists(data, Function(x) x Mod 2 = 0)
End Function
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
  <li> Microsoft Learn - <a
  href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.exists">List&lt;T&gt;.Exists(Predicate&lt;T&gt;)</a> </li>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/api/system.array.exists">Array.Exists&lt;T&gt;(T[], Predicate&lt;T&gt;)</a>
  </li>
  <li> Microsoft Learn - <a
  href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.exists">ImmutableList&lt;T&gt;.Exists(Predicate&lt;T&gt;)</a> </li>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any">Enumerable.Any(Predicate&lt;T&gt;)</a>
  </li>
  <li> Microsoft Learn - <a href="https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/linq-to-entities">LINQ to
  Entities</a> </li>
</ul>
<h3>Benchmarks</h3>
<table>
  <colgroup>
    <col style="width: 16.6666%;">
    <col style="width: 16.6666%;">
    <col style="width: 16.6666%;">
    <col style="width: 16.6666%;">
    <col style="width: 16.6666%;">
    <col style="width: 16.667%;">
  </colgroup>
  <thead>
    <tr>
      <th>Method</th>
      <th>Runtime</th>
      <th>Categories</th>
      <th>Mean</th>
      <th>Standard Deviation</th>
      <th>Allocated</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><p>ArrayAny</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>Array</p></td>
      <td><p>1,174.0 ns</p></td>
      <td><p>16.44 ns</p></td>
      <td><p>32 B</p></td>
    </tr>
    <tr>
      <td><p>ArrayExists</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>Array</p></td>
      <td><p>570.6 ns</p></td>
      <td><p>7.12 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ArrayAny</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>Array</p></td>
      <td><p>358.5 ns</p></td>
      <td><p>5.57 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td><p>ArrayExists</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>Array</p></td>
      <td><p>581.6 ns</p></td>
      <td><p>6.17 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ArrayAny</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>Array</p></td>
      <td><p>4,896.0 ns</p></td>
      <td><p>102.83 ns</p></td>
      <td><p>32 B</p></td>
    </tr>
    <tr>
      <td><p>ArrayExists</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>Array</p></td>
      <td><p>1,649.4 ns</p></td>
      <td><p>29.81 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ImmutableListAny</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>7,859.3 ns</p></td>
      <td><p>91.45 ns</p></td>
      <td><p>72 B</p></td>
    </tr>
    <tr>
      <td><p>ImmutableListExists</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>5,898.1 ns</p></td>
      <td><p>81.69 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ImmutableListAny</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>7,748.9 ns</p></td>
      <td><p>119.10 ns</p></td>
      <td><p>72 B</p></td>
    </tr>
    <tr>
      <td><p>ImmutableListExists</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>5,705.0 ns</p></td>
      <td><p>31.53 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ImmutableListAny</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>45,118.5 ns</p></td>
      <td><p>168.72 ns</p></td>
      <td><p>72 B</p></td>
    </tr>
    <tr>
      <td><p>ImmutableListExists</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>ImmutableList&lt;T&gt;</p></td>
      <td><p>41,966.0 ns</p></td>
      <td><p>631.59 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ListAny</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>1,643.5 ns</p></td>
      <td><p>13.09 ns</p></td>
      <td><p>40 B</p></td>
    </tr>
    <tr>
      <td><p>ListExists</p></td>
      <td><p>.NET 8.0</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>726.2 ns</p></td>
      <td><p>11.99 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ListAny</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>398.6 ns</p></td>
      <td><p>8.20 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td><p>ListExists</p></td>
      <td><p>.NET 9.0</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>612.4 ns</p></td>
      <td><p>18.73 ns</p></td>
      <td><p>-</p></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td><p>ListAny</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>5,621.5 ns</p></td>
      <td><p>35.80 ns</p></td>
      <td><p>40 B</p></td>
    </tr>
    <tr>
      <td><p>ListExists</p></td>
      <td><p>.NET Framework 4.8.1</p></td>
      <td><p>List&lt;T&gt;</p></td>
      <td><p>1,748.0 ns</p></td>
      <td><p>11.76 ns</p></td>
      <td><p>-</p></td>
    </tr>
  </tbody>
</table>
<h4>Glossary</h4>
<ul>
  <li> <a href="https://en.wikipedia.org/wiki/Arithmetic_mean">Mean</a> </li>
  <li> <a href="https://en.wikipedia.org/wiki/Standard_deviation">Standard Deviation</a> </li>
  <li> <a href="https://en.wikipedia.org/wiki/Memory_management">Allocated</a> </li>
</ul>
<p>The results were generated by running the following snippet with <a href="https://github.com/dotnet/BenchmarkDotNet">BenchmarkDotNet</a>:</p>
<pre>
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func&lt;int, bool&gt; ConditionFunc = static x =&gt; x == -1 * Math.Abs(x);
private readonly static Predicate&lt;int&gt; ConditionPredicate = static x =&gt; x == -1 * Math.Abs(x);

private List&lt;int&gt; list;
private ImmutableList&lt;int&gt; immutableList;
private int[] array;

[Params(1_000)]
public int N { get; set; }

[GlobalSetup]
public void GlobalSetup()
{
    list = Enumerable.Range(0, N).Select(x =&gt; N - x).ToList();
    immutableList = ImmutableList.CreateRange(list);
    array = list.ToArray();
}

[BenchmarkCategory("List&lt;T&gt;"), Benchmark]
public bool ListAny() =&gt;
    list.Any(ConditionFunc);

[BenchmarkCategory("List&lt;T&gt;"), Benchmark(Baseline = true)]
public bool ListExists() =&gt;
    list.Exists(ConditionPredicate);

[BenchmarkCategory("ImmutableList&lt;T&gt;"), Benchmark(Baseline = true)]
public bool ImmutableListAny() =&gt;
    immutableList.Any(ConditionFunc);

[BenchmarkCategory("ImmutableList&lt;T&gt;"), Benchmark]
public bool ImmutableListExists() =&gt;
    immutableList.Exists(ConditionPredicate);

[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAny() =&gt;
    array.Any(ConditionFunc);

[BenchmarkCategory("Array"), Benchmark]
public bool ArrayExists() =&gt;
    Array.Exists(array, ConditionPredicate);
</pre>
<p>Hardware configuration:</p>
<pre>
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
  [Host]               : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256
  .NET 8.0             : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 9.0             : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256
</pre>

